import xml.etree.ElementTree as ET
from xml.etree.ElementTree import Element
from .exceptions import XmlElementNotFound, MsgBrokenException, MsgTypeUnSupported

class __XmlElement__(Element):
    def __init__(self, xml_str : str) -> None:
        self.__xml_element__ = ET.fromstring(xml_str)


    def find(self, path: str, namespaces: dict[str, str] = None) -> Element:
        found_element = self.__xml_element__.find(path, namespaces)
        if found_element is None:
            raise XmlElementNotFound('Can not find {}'.format(path))

        return found_element


class Msg(object):
    def __init__(self, xmlData : __XmlElement__):
        try:
            self.ToUserName = xmlData.find('ToUserName').text
            self.FromUserName = xmlData.find('FromUserName').text
            self.CreateTime = xmlData.find('CreateTime').text
            self.MsgType = xmlData.find('MsgType').text
            self.MsgId = xmlData.find('MsgId').text
        except XmlElementNotFound as e:
            raise MsgBrokenException('Message might be broken! {}'.format(e.args))


class TextMsg(Msg):
    def __init__(self, xmlData : __XmlElement__):
        super(TextMsg, self).__init__(xmlData)
        try:
            self.Content = xmlData.find('Content').text
        except XmlElementNotFound as e:
            raise MsgBrokenException('Message might be broken! {}'.format(e.args))


class ImageMsg(Msg):
    def __init__(self, xmlData : __XmlElement__):
        super(ImageMsg, self).__init__(xmlData)
        try:
            self.PicUrl = xmlData.find('PicUrl').text
            self.MediaId = xmlData.find('MediaId').text
        except XmlElementNotFound as e:
            raise MsgBrokenException('Message might be broken! {}'.format(e.args))


class EventMsg(Msg):
    def __init__(self, xmlData: __XmlElement__):
        super(EventMsg, self).__init__(xmlData)
        self.Event = xmlData.find('Event').text


class VoiceMsg(Msg):
    def __init__(self, xmlData: __XmlElement__):
        super(VoiceMsg, self).__init__(xmlData)
        self.MediaId = xmlData.find('MediaId').text
        self.Format = xmlData.find('Format').text
        self.Recognition = xmlData.find('Recognition').text


def FromXml(xml_string : str) -> TextMsg | ImageMsg | EventMsg | VoiceMsg:
    class_mapping = {
        'text' : TextMsg,
        'image' : ImageMsg,
        'event' : EventMsg,
        'voice' : VoiceMsg
    }

    xml_data = __XmlElement__(xml_string)
    msg_type = xml_data.find('MsgType').text
    if msg_type not in class_mapping:
        raise MsgTypeUnSupported(f'{msg_type} is unsupported message type')

    return class_mapping[msg_type](xml_data)

